// jobs
package common

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"time"
)

var httpc *http.Client = &http.Client{
	Transport: &http.Transport{
		Dial: func(netw, addr string) (net.Conn, error) {
			deadline := time.Now().Add(25 * time.Second)
			c, err := net.DialTimeout(netw, addr, time.Second*20)
			if err != nil {
				return nil, err
			}
			c.SetDeadline(deadline)
			return c, nil
		},
	},
}

func HttpPoster(url string, data map[string][]string) (string, error) {
	resp, err := httpc.PostForm(url, data)
    defer resp.Body.Close()
	if err != nil {
		return "", err
	}
	if resp.StatusCode != 200 {
		return "", fmt.Errorf("HTTPErrorcode:%d", resp.StatusCode)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	return string(body), nil
}

func HttpGetter(url string, data map[string][]string) (string, error) {
	resp, err := httpc.Get(url)
    defer resp.Body.Close()
	if err != nil {
		return "", err
	}
	if resp.StatusCode != 200 {
		return "", fmt.Errorf("HTTPErrorcode:%d", resp.StatusCode)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	return string(body), nil
}

type Job interface {
	OnStart(baseurl string, info string) error
	OnTick(baseurl string, params map[string]string) error
	OnStop(baseurl string) error
	ShouldExit() bool
	ShouldRestart() bool
	Started() bool
}

type HeartBeater struct {
	url     string
	getter  Getter
	state   string
	Checker Checker //check if need to run
}

func (h *HeartBeater) OnStart(baseurl string, info string) error {
	if h.getter == nil {
		h.getter = HttpPoster
	}
	if h.state == "" {
		ip, err := GetAddr(baseurl)
		if err != nil {
			ip = ""
		}
		t := fmt.Sprintf("%d", time.Now().UnixNano())
		h.state, err = GetstringMd5(ip + t)
		h.state = h.state + "." + info
		if err != nil {
			h.state = t
		}
	}
	h.url = fmt.Sprintf("%s/heartbeat/Golia", baseurl)
	return nil
}

func (h *HeartBeater) OnTick(baseurl string, params map[string]string) error {
	v := map[string][]string{"state": {h.state}}
	_, err := h.getter(h.url, v)
	return err
}

func (h *HeartBeater) OnStop(baseurl string) error {
	h.state = ""
	return nil
}

func (h *HeartBeater) ShouldExit() bool {
	return false
}

func (h *HeartBeater) ShouldRestart() bool {
	return false
}

func (h *HeartBeater) Started() bool {
	return h.Checker() && h.state != ""
}

//sync & restart job

type ChangeDetector struct {
	WatchFileSet map[string]string //url->actutalfile
	LocalFileSet map[string]string //url->md5
	url          string
	restartflag  bool
	Getter       Getter
	Sendnum      int
}

func NewChangeDetector(watchSet map[string]string) (*ChangeDetector, error) {
	l := make(map[string]string)
	w := make(map[string]string)
	for u, f := range watchSet {
		md5, err := GetMd5(f)
		if err != nil {
			return nil, err
		}
		l[u] = md5
		w[u] = f
	}
	return &ChangeDetector{WatchFileSet: w, LocalFileSet: l, restartflag: false}, nil
}

func (c *ChangeDetector) OnStart(baseurl string, info string) error {
	if c.Getter == nil {
		c.Getter = HttpGetter
	}
	c.url = fmt.Sprintf("%s/sync/", baseurl)
	return nil
}

func (c *ChangeDetector) OnTick(baseurl string, params map[string]string) error {
	for k, v := range c.WatchFileSet {
		newmd5, err := c.Getter(c.url+k+"?md5only=true", nil)
		if err != nil {
			return err
		}
		if newmd5 != c.LocalFileSet[k] && newmd5 != "" && newmd5 != "{}" {
			fmt.Println(k, " -- new:"+newmd5, c.LocalFileSet[k], c.url+k, v)
			c.restartflag = true
			if err := Update(c.url+k, v, 0); err != nil {
				return err
			}
		}
	}
	return nil
}

func (c *ChangeDetector) OnStop(baseurl string) error {
	c.url = ""
	c.WatchFileSet = nil
	c.LocalFileSet = nil
	return nil
}

func (c *ChangeDetector) ShouldExit() bool {
	return false
}

func (c *ChangeDetector) ShouldRestart() bool {
	return c.restartflag
}

func (c *ChangeDetector) Started() bool {
	if c.Sendnum < 720 && c.Sendnum != 0 {
		c.Sendnum++
		return false
	} else {
		c.Sendnum = 1
	}
	return c.url != "" && len(c.WatchFileSet) != 0 && len(c.LocalFileSet) != 0
}
